/*!
* jQuery BBQ: Back Button & Query Library - v1.3pre - 8/26/2010
* http://benalman.com/projects/jquery-bbq-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/

// Script: jQuery BBQ: Back Button & Query Library
//
// *Version: 1.3pre, Last updated: 8/26/2010*
//
// Project Home - http://benalman.com/projects/jquery-bbq-plugin/
// GitHub       - http://github.com/cowboy/jquery-bbq/
// Source       - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.js
// (Minified)   - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.min.js (2.2kb gzipped)
//
// About: License
//
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
//
// About: Examples
//
// These working examples, complete with fully commented code, illustrate a few
// ways in which this plugin can be used.
//
// Basic AJAX     - http://benalman.com/code/projects/jquery-bbq/examples/fragment-basic/
// Advanced AJAX  - http://benalman.com/code/projects/jquery-bbq/examples/fragment-advanced/
// jQuery UI Tabs - http://benalman.com/code/projects/jquery-bbq/examples/fragment-jquery-ui-tabs/
// Deparam        - http://benalman.com/code/projects/jquery-bbq/examples/deparam/
//
// About: Support and Testing
//
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
//
// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
//                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
// Unit Tests      - http://benalman.com/code/projects/jquery-bbq/unit/
//
// About: Release History
//
// 1.3pre - (8/26/2010) Integrated <jQuery hashchange event> v1.3, which adds
//         document.title and document.domain support in IE6/7, BlackBerry
//         support, better Iframe hiding for accessibility reasons, and the new
//         <jQuery.fn.hashchange> "shortcut" method. Added the
//         <jQuery.param.sorted> method which reduces the possibility of
//         extraneous hashchange event triggering. Added the
//         <jQuery.param.fragment.ajaxCrawlable> method which can be used to
//         enable Google "AJAX Crawlable mode."
// 1.2.1 - (2/17/2010) Actually fixed the stale window.location Safari bug from
//         <jQuery hashchange event> in BBQ, which was the main reason for the
//         previous release!
// 1.2   - (2/16/2010) Integrated <jQuery hashchange event> v1.2, which fixes a
//         Safari bug, the event can now be bound before DOM ready, and IE6/7
//         page should no longer scroll when the event is first bound. Also
//         added the <jQuery.param.fragment.noEscape> method, and reworked the
//         <hashchange event (BBQ)> internal "add" method to be compatible with
//         changes made to the jQuery 1.4.2 special events API.
// 1.1.1 - (1/22/2010) Integrated <jQuery hashchange event> v1.1, which fixes an
//         obscure IE8 EmulateIE7 meta tag compatibility mode bug.
// 1.1   - (1/9/2010) Broke out the jQuery BBQ event.special <hashchange event>
//         functionality into a separate plugin for users who want just the
//         basic event & back button support, without all the extra awesomeness
//         that BBQ provides. This plugin will be included as part of jQuery BBQ,
//         but also be available separately. See <jQuery hashchange event>
//         plugin for more information. Also added the <jQuery.bbq.removeState>
//         method and added additional <jQuery.deparam> examples.
// 1.0.3 - (12/2/2009) Fixed an issue in IE 6 where location.search and
//         location.hash would report incorrectly if the hash contained the ?
//         character. Also <jQuery.param.querystring> and <jQuery.param.fragment>
//         will no longer parse params out of a URL that doesn't contain ? or #,
//         respectively.
// 1.0.2 - (10/10/2009) Fixed an issue in IE 6/7 where the hidden IFRAME caused
//         a "This page contains both secure and nonsecure items." warning when
//         used on an https:// page.
// 1.0.1 - (10/7/2009) Fixed an issue in IE 8. Since both "IE7" and "IE8
//         Compatibility View" modes erroneously report that the browser
//         supports the native window.onhashchange event, a slightly more
//         robust test needed to be added.
// 1.0   - (10/2/2009) Initial release

(function($,window){
	'$:nomunge'; // Used by YUI compressor.

	// Some convenient shortcuts.
	var undefined,
	aps = Array.prototype.slice,
	decode = decodeURIComponent,

	// Method / object references.
	jq_param = $.param,
	jq_param_sorted,
	jq_param_fragment,
	jq_deparam,
	jq_deparam_fragment,
	jq_bbq = $.bbq = $.bbq || {},
	jq_bbq_pushState,
	jq_bbq_getState,
	jq_elemUrlAttr,
	special = $.event.special,

	// Reused strings.
	str_hashchange = 'hashchange',
	str_querystring = 'querystring',
	str_fragment = 'fragment',
	str_elemUrlAttr = 'elemUrlAttr',
	str_href = 'href',
	str_src = 'src',

	// Reused RegExp.
	re_params_querystring = /^.*\?|#.*$/g,
	re_params_fragment,
	re_fragment,
	re_no_escape,

	ajax_crawlable,
	fragment_prefix,

	// Used by jQuery.elemUrlAttr.
	elemUrlAttr_cache = {};

	// A few commonly used bits, broken out to help reduce minified file size.

	function is_string( arg ) {
		return typeof arg === 'string';
	};

	// Why write the same function twice? Let's curry! Mmmm, curry..

	function curry( func ) {
		var args = aps.call( arguments, 1 );

		return function() {
			return func.apply( this, args.concat( aps.call( arguments ) ) );
		};
	};

	// Get location.hash (or what you'd expect location.hash to be) sans any
	// leading #. Thanks for making this necessary, Firefox!
	function get_fragment( url ) {
		return url.replace( re_fragment, '$2' );
	};

	// Get location.search (or what you'd expect location.search to be) sans any
	// leading #. Thanks for making this necessary, IE6!
	function get_querystring( url ) {
		return url.replace( /(?:^[^?#]*\?([^#]*).*$)?.*/, '$1' );
	};

	// Section: Param (to string)
	//
	// Method: jQuery.param.querystring
	//
	// Retrieve the query string from a URL or if no arguments are passed, the
	// current window.location.href.
	//
	// Usage:
	//
	// > jQuery.param.querystring( [ url ] );
	//
	// Arguments:
	//
	//  url - (String) A URL containing query string params to be parsed. If url
	//    is not passed, the current window.location.href is used.
	//
	// Returns:
	//
	//  (String) The parsed query string, with any leading "?" removed.
	//

	// Method: jQuery.param.querystring (build url)
	//
	// Merge a URL, with or without pre-existing query string params, plus any
	// object, params string or URL containing query string params into a new URL.
	//
	// Usage:
	//
	// > jQuery.param.querystring( url, params [, merge_mode ] );
	//
	// Arguments:
	//
	//  url - (String) A valid URL for params to be merged into. This URL may
	//    contain a query string and/or fragment (hash).
	//  params - (String) A params string or URL containing query string params to
	//    be merged into url.
	//  params - (Object) A params object to be merged into url.
	//  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
	//    specified, and is as-follows:
	//
	//    * 0: params in the params argument will override any query string
	//         params in url.
	//    * 1: any query string params in url will override params in the params
	//         argument.
	//    * 2: params argument will completely replace any query string in url.
	//
	// Returns:
	//
	//  (String) A URL with a urlencoded query string in the format '?a=b&c=d&e=f'.

	// Method: jQuery.param.fragment
	//
	// Retrieve the fragment (hash) from a URL or if no arguments are passed, the
	// current window.location.href.
	//
	// Usage:
	//
	// > jQuery.param.fragment( [ url ] );
	//
	// Arguments:
	//
	//  url - (String) A URL containing fragment (hash) params to be parsed. If
	//    url is not passed, the current window.location.href is used.
	//
	// Returns:
	//
	//  (String) The parsed fragment (hash) string, with any leading "#" removed.

	// Method: jQuery.param.fragment (build url)
	//
	// Merge a URL, with or without pre-existing fragment (hash) params, plus any
	// object, params string or URL containing fragment (hash) params into a new
	// URL.
	//
	// Usage:
	//
	// > jQuery.param.fragment( url, params [, merge_mode ] );
	//
	// Arguments:
	//
	//  url - (String) A valid URL for params to be merged into. This URL may
	//    contain a query string and/or fragment (hash).
	//  params - (String) A params string or URL containing fragment (hash) params
	//    to be merged into url.
	//  params - (Object) A params object to be merged into url.
	//  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
	//    specified, and is as-follows:
	//
	//    * 0: params in the params argument will override any fragment (hash)
	//         params in url.
	//    * 1: any fragment (hash) params in url will override params in the
	//         params argument.
	//    * 2: params argument will completely replace any query string in url.
	//
	// Returns:
	//
	//  (String) A URL with a urlencoded fragment (hash) in the format '#a=b&c=d&e=f'.

	function jq_param_sub( is_fragment, get_func, url, params, merge_mode ) {
		var result,
		qs,
		matches,
		url_params,
		hash;

		if ( params !== undefined ) {
			// Build URL by merging params into url string.

			// matches[1] = url part that precedes params, not including trailing ?/#
			// matches[2] = params, not including leading ?/#
			// matches[3] = if in 'querystring' mode, hash including leading #, otherwise ''
			matches = url.match( is_fragment ? re_fragment : /^([^#?]*)\??([^#]*)(#?.*)/ );

			// Get the hash if in 'querystring' mode, and it exists.
			hash = matches[3] || '';

			if ( merge_mode === 2 && is_string( params ) ) {
				// If merge_mode is 2 and params is a string, merge the fragment / query
				// string into the URL wholesale, without converting it into an object.
				qs = params.replace( is_fragment ? re_params_fragment : re_params_querystring, '' );

			} else {
				// Convert relevant params in url to object.
				url_params = jq_deparam( matches[2] );

				params = is_string( params )

				// Convert passed params string into object.
				? jq_deparam[ is_fragment ? str_fragment : str_querystring ]( params )

				// Passed params object.
				: params;

				qs = merge_mode === 2 ? params                              // passed params replace url params
				: merge_mode === 1  ? $.extend( {}, params, url_params )  // url params override passed params
				: $.extend( {}, url_params, params );                     // passed params override url params

				// Convert params object into a sorted params string.
				qs = jq_param_sorted( qs );

				// Unescape characters specified via $.param.noEscape. Since only hash-
				// history users have requested this feature, it's only enabled for
				// fragment-related params strings.
				if ( is_fragment ) {
					qs = qs.replace( re_no_escape, decode );
				}
			}

			// Build URL from the base url, querystring and hash. In 'querystring'
			// mode, ? is only added if a query string exists. In 'fragment' mode, #
			// is always added.
			result = matches[1] + ( is_fragment ? fragment_prefix : qs || !matches[1] ? '?' : '' ) + qs + hash;

		} else {
			// If URL was passed in, parse params from URL string, otherwise parse
			// params from window.location.href.
			result = get_func( url !== undefined ? url : location.href );
		}

		return result;
	};

	jq_param[ str_querystring ]                  = curry( jq_param_sub, 0, get_querystring );
	jq_param[ str_fragment ] = jq_param_fragment = curry( jq_param_sub, 1, get_fragment );

	// Method: jQuery.param.sorted
	//
	// Returns a params string equivalent to that returned by the internal
	// jQuery.param method, but sorted, which makes it suitable for use as a
	// cache key.
	//
	// For example, in most browsers jQuery.param({z:1,a:2}) returns "z=1&a=2"
	// and jQuery.param({a:2,z:1}) returns "a=2&z=1". Even though both the
	// objects being serialized and the resulting params strings are equivalent,
	// if these params strings were set into the location.hash fragment
	// sequentially, the hashchange event would be triggered unnecessarily, since
	// the strings are different (even though the data described by them is the
	// same). By sorting the params string, unecessary hashchange event triggering
	// can be avoided.
	//
	// Usage:
	//
	// > jQuery.param.sorted( obj [, traditional ] );
	//
	// Arguments:
	//
	//  obj - (Object) An object to be serialized.
	//  traditional - (Boolean) Params deep/shallow serialization mode. See the
	//    documentation at http://api.jquery.com/jQuery.param/ for more detail.
	//
	// Returns:
	//
	//  (String) A sorted params string.

	jq_param.sorted = jq_param_sorted = function( a, traditional ) {
		var arr = [],
		obj = {};

		$.each( jq_param( a, traditional ).split( '&' ), function(i,v){
			var key = v.replace( /(?:%5B|=).*$/, '' ),
			key_obj = obj[ key ];

			if ( !key_obj ) {
				key_obj = obj[ key ] = [];
				arr.push( key );
			}

			key_obj.push( v );
		});

		return $.map( arr.sort(), function(v){
			return obj[ v ];
		}).join( '&' );
	};

	// Method: jQuery.param.fragment.noEscape
	//
	// Specify characters that will be left unescaped when fragments are created
	// or merged using <jQuery.param.fragment>, or when the fragment is modified
	// using <jQuery.bbq.pushState>. This option only applies to serialized data
	// object fragments, and not set-as-string fragments. Does not affect the
	// query string. Defaults to ",/" (comma, forward slash).
	//
	// Note that this is considered a purely aesthetic option, and will help to
	// create URLs that "look pretty" in the address bar or bookmarks, without
	// affecting functionality in any way. That being said, be careful to not
	// unescape characters that are used as delimiters or serve a special
	// purpose, such as the "#?&=+" (octothorpe, question mark, ampersand,
	// equals, plus) characters.
	//
	// Usage:
	//
	// > jQuery.param.fragment.noEscape( [ chars ] );
	//
	// Arguments:
	//
	//  chars - (String) The characters to not escape in the fragment. If
	//    unspecified, defaults to empty string (escape all characters).
	//
	// Returns:
	//
	//  Nothing.

	jq_param_fragment.noEscape = function( chars ) {
		chars = chars || '';
		var arr = $.map( chars.split(''), encodeURIComponent );
		re_no_escape = new RegExp( arr.join('|'), 'g' );
	};

	// A sensible default. These are the characters people seem to complain about
	// "uglifying up the URL" the most.
	jq_param_fragment.noEscape( ',/' );

	// Method: jQuery.param.fragment.ajaxCrawlable
	//
	// TODO: DESCRIBE
	//
	// Usage:
	//
	// > jQuery.param.fragment.ajaxCrawlable( [ state ] );
	//
	// Arguments:
	//
	//  state - (Boolean) TODO: DESCRIBE
	//
	// Returns:
	//
	//  (Boolean) The current ajaxCrawlable state.

	jq_param_fragment.ajaxCrawlable = function( state ) {
		if ( state !== undefined ) {
			if ( state ) {
				re_params_fragment = /^.*(?:#!|#)/;
				re_fragment = /^([^#]*)(?:#!|#)?(.*)$/;
				fragment_prefix = '#!';
			} else {
				re_params_fragment = /^.*#/;
				re_fragment = /^([^#]*)#?(.*)$/;
				fragment_prefix = '#';
			}
			ajax_crawlable = !!state;
		}

		return ajax_crawlable;
	};

	jq_param_fragment.ajaxCrawlable( 0 );

	// Section: Deparam (from string)
	//
	// Method: jQuery.deparam
	//
	// Deserialize a params string into an object, optionally coercing numbers,
	// booleans, null and undefined values; this method is the counterpart to the
	// internal jQuery.param method.
	//
	// Usage:
	//
	// > jQuery.deparam( params [, coerce ] );
	//
	// Arguments:
	//
	//  params - (String) A params string to be parsed.
	//  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
	//    undefined to their actual value. Defaults to false if omitted.
	//
	// Returns:
	//
	//  (Object) An object representing the deserialized params string.

	$.deparam = jq_deparam = function( params, coerce ) {
		var obj = {},
		coerce_types = { 'true': !0, 'false': !1, 'null': null };

		// Iterate over all name=value pairs.
		$.each( params.replace( /\+/g, ' ' ).split( '&' ), function(j,v){
			var param = v.split( '=' ),
			key = decode( param[0] ),
			val,
			cur = obj,
			i = 0,

			// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
			// into its component parts.
			keys = key.split( '][' ),
			keys_last = keys.length - 1;

			// If the first keys part contains [ and the last ends with ], then []
			// are correctly balanced.
			if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) {
				// Remove the trailing ] from the last keys part.
				keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' );

				// Split first keys part into two parts on the [ and add them back onto
				// the beginning of the keys array.
				keys = keys.shift().split('[').concat( keys );

				keys_last = keys.length - 1;
			} else {
				// Basic 'foo' style key.
				keys_last = 0;
			}

			// Are we dealing with a name=value pair, or just a name?
			if ( param.length === 2 ) {
				val = decode( param[1] );

				// Coerce values.
				if ( coerce ) {
					val = val && !isNaN(val)            ? +val              // number
					: val === 'undefined'             ? undefined         // undefined
					: coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
					: val;                                                // string
				}

				if ( keys_last ) {
					// Complex key, build deep object structure based on a few rules:
					// * The 'cur' pointer starts at the object top-level.
					// * [] = array push (n is set to array length), [n] = array if n is
					//   numeric, otherwise object.
					// * If at the last keys part, set the value.
					// * For each keys part, if the current level is undefined create an
					//   object or array based on the type of the next keys part.
					// * Move the 'cur' pointer to the next level.
					// * Rinse & repeat.
					for ( ; i <= keys_last; i++ ) {
						key = keys[i] === '' ? cur.length : keys[i];
						cur = cur[key] = i < keys_last
						? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
						: val;
					}

				} else {
					// Simple key, even simpler rules, since only scalars and shallow
					// arrays are allowed.

					if ( $.isArray( obj[key] ) ) {
						// val is already an array, so push on the next value.
						obj[key].push( val );

					} else if ( obj[key] !== undefined ) {
						// val isn't an array, but since a second value has been specified,
						// convert val into an array.
						obj[key] = [ obj[key], val ];

					} else {
						// val is a scalar.
						obj[key] = val;
					}
				}

			} else if ( key ) {
				// No value was defined, so set something meaningful.
				obj[key] = coerce
				? undefined
				: '';
			}
		});

		return obj;
	};

	// Method: jQuery.deparam.querystring
	//
	// Parse the query string from a URL or the current window.location.href,
	// deserializing it into an object, optionally coercing numbers, booleans,
	// null and undefined values.
	//
	// Usage:
	//
	// > jQuery.deparam.querystring( [ url ] [, coerce ] );
	//
	// Arguments:
	//
	//  url - (String) An optional params string or URL containing query string
	//    params to be parsed. If url is omitted, the current
	//    window.location.href is used.
	//  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
	//    undefined to their actual value. Defaults to false if omitted.
	//
	// Returns:
	//
	//  (Object) An object representing the deserialized params string.

	// Method: jQuery.deparam.fragment
	//
	// Parse the fragment (hash) from a URL or the current window.location.href,
	// deserializing it into an object, optionally coercing numbers, booleans,
	// null and undefined values.
	//
	// Usage:
	//
	// > jQuery.deparam.fragment( [ url ] [, coerce ] );
	//
	// Arguments:
	//
	//  url - (String) An optional params string or URL containing fragment (hash)
	//    params to be parsed. If url is omitted, the current window.location.href
	//    is used.
	//  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
	//    undefined to their actual value. Defaults to false if omitted.
	//
	// Returns:
	//
	//  (Object) An object representing the deserialized params string.

	function jq_deparam_sub( is_fragment, url_or_params, coerce ) {
		if ( url_or_params === undefined || typeof url_or_params === 'boolean' ) {
			// url_or_params not specified.
			coerce = url_or_params;
			url_or_params = jq_param[ is_fragment ? str_fragment : str_querystring ]();
		} else {
			url_or_params = is_string( url_or_params )
			? url_or_params.replace( is_fragment ? re_params_fragment : re_params_querystring, '' )
			: url_or_params;
		}

		return jq_deparam( url_or_params, coerce );
	};

	jq_deparam[ str_querystring ]                    = curry( jq_deparam_sub, 0 );
	jq_deparam[ str_fragment ] = jq_deparam_fragment = curry( jq_deparam_sub, 1 );

	// Section: Element manipulation
	//
	// Method: jQuery.elemUrlAttr
	//
	// Get the internal "Default URL attribute per tag" list, or augment the list
	// with additional tag-attribute pairs, in case the defaults are insufficient.
	//
	// In the <jQuery.fn.querystring> and <jQuery.fn.fragment> methods, this list
	// is used to determine which attribute contains the URL to be modified, if
	// an "attr" param is not specified.
	//
	// Default Tag-Attribute List:
	//
	//  a      - href
	//  base   - href
	//  iframe - src
	//  img    - src
	//  input  - src
	//  form   - action
	//  link   - href
	//  script - src
	//
	// Usage:
	//
	// > jQuery.elemUrlAttr( [ tag_attr ] );
	//
	// Arguments:
	//
	//  tag_attr - (Object) An object containing a list of tag names and their
	//    associated default attribute names in the format { tag: 'attr', ... } to
	//    be merged into the internal tag-attribute list.
	//
	// Returns:
	//
	//  (Object) An object containing all stored tag-attribute values.

	// Only define function and set defaults if function doesn't already exist, as
	// the urlInternal plugin will provide this method as well.
	$[ str_elemUrlAttr ] || ($[ str_elemUrlAttr ] = function( obj ) {
		return $.extend( elemUrlAttr_cache, obj );
	})({
		a: str_href,
		base: str_href,
		iframe: str_src,
		img: str_src,
		input: str_src,
		form: 'action',
		link: str_href,
		script: str_src
	});

	jq_elemUrlAttr = $[ str_elemUrlAttr ];

	// Method: jQuery.fn.querystring
	//
	// Update URL attribute in one or more elements, merging the current URL (with
	// or without pre-existing query string params) plus any params object or
	// string into a new URL, which is then set into that attribute. Like
	// <jQuery.param.querystring (build url)>, but for all elements in a jQuery
	// collection.
	//
	// Usage:
	//
	// > jQuery('selector').querystring( [ attr, ] params [, merge_mode ] );
	//
	// Arguments:
	//
	//  attr - (String) Optional name of an attribute that will contain a URL to
	//    merge params or url into. See <jQuery.elemUrlAttr> for a list of default
	//    attributes.
	//  params - (Object) A params object to be merged into the URL attribute.
	//  params - (String) A URL containing query string params, or params string
	//    to be merged into the URL attribute.
	//  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
	//    specified, and is as-follows:
	//
	//    * 0: params in the params argument will override any params in attr URL.
	//    * 1: any params in attr URL will override params in the params argument.
	//    * 2: params argument will completely replace any query string in attr
	//         URL.
	//
	// Returns:
	//
	//  (jQuery) The initial jQuery collection of elements, but with modified URL
	//  attribute values.

	// Method: jQuery.fn.fragment
	//
	// Update URL attribute in one or more elements, merging the current URL (with
	// or without pre-existing fragment/hash params) plus any params object or
	// string into a new URL, which is then set into that attribute. Like
	// <jQuery.param.fragment (build url)>, but for all elements in a jQuery
	// collection.
	//
	// Usage:
	//
	// > jQuery('selector').fragment( [ attr, ] params [, merge_mode ] );
	//
	// Arguments:
	//
	//  attr - (String) Optional name of an attribute that will contain a URL to
	//    merge params into. See <jQuery.elemUrlAttr> for a list of default
	//    attributes.
	//  params - (Object) A params object to be merged into the URL attribute.
	//  params - (String) A URL containing fragment (hash) params, or params
	//    string to be merged into the URL attribute.
	//  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
	//    specified, and is as-follows:
	//
	//    * 0: params in the params argument will override any params in attr URL.
	//    * 1: any params in attr URL will override params in the params argument.
	//    * 2: params argument will completely replace any fragment (hash) in attr
	//         URL.
	//
	// Returns:
	//
	//  (jQuery) The initial jQuery collection of elements, but with modified URL
	//  attribute values.

	function jq_fn_sub( mode, force_attr, params, merge_mode ) {
		if ( !is_string( params ) && typeof params !== 'object' ) {
			// force_attr not specified.
			merge_mode = params;
			params = force_attr;
			force_attr = undefined;
		}

		return this.each(function(){
			var that = $(this),

			// Get attribute specified, or default specified via $.elemUrlAttr.
			attr = force_attr || jq_elemUrlAttr()[ ( this.nodeName || '' ).toLowerCase() ] || '',

			// Get URL value.
			url = attr && that.attr( attr ) || '';

			// Update attribute with new URL.
			that.attr( attr, jq_param[ mode ]( url, params, merge_mode ) );
		});

	};

	$.fn[ str_querystring ] = curry( jq_fn_sub, str_querystring );
	$.fn[ str_fragment ]    = curry( jq_fn_sub, str_fragment );

	// Section: History, hashchange event
	//
	// Method: jQuery.bbq.pushState
	//
	// Adds a 'state' into the browser history at the current position, setting
	// location.hash and triggering any bound <hashchange event> callbacks
	// (provided the new state is different than the previous state).
	//
	// If no arguments are passed, an empty state is created, which is just a
	// shortcut for jQuery.bbq.pushState( {}, 2 ).
	//
	// Usage:
	//
	// > jQuery.bbq.pushState( [ params [, merge_mode ] ] );
	//
	// Arguments:
	//
	//  params - (String) A serialized params string or a hash string beginning
	//    with # to merge into location.hash.
	//  params - (Object) A params object to merge into location.hash.
	//  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
	//    specified (unless a hash string beginning with # is specified, in which
	//    case merge behavior defaults to 2), and is as-follows:
	//
	//    * 0: params in the params argument will override any params in the
	//         current state.
	//    * 1: any params in the current state will override params in the params
	//         argument.
	//    * 2: params argument will completely replace current state.
	//
	// Returns:
	//
	//  Nothing.
	//
	// Additional Notes:
	//
	//  * Setting an empty state may cause the browser to scroll.
	//  * Unlike the fragment and querystring methods, if a hash string beginning
	//    with # is specified as the params agrument, merge_mode defaults to 2.

	jq_bbq.pushState = jq_bbq_pushState = function( params, merge_mode ) {
		if ( is_string( params ) && /^#/.test( params ) && merge_mode === undefined ) {
			// Params string begins with # and merge_mode not specified, so completely
			// overwrite window.location.hash.
			merge_mode = 2;
		}

		var has_args = params !== undefined,
		// Merge params into window.location using $.param.fragment.
		url = jq_param_fragment( location.href,
		has_args ? params : {}, has_args ? merge_mode : 2 );

		// Set new window.location.href. Note that Safari 3 & Chrome barf on
		// location.hash = '#' so the entire URL is set.
		location.href = url;
	};

	// Method: jQuery.bbq.getState
	//
	// Retrieves the current 'state' from the browser history, parsing
	// location.hash for a specific key or returning an object containing the
	// entire state, optionally coercing numbers, booleans, null and undefined
	// values.
	//
	// Usage:
	//
	// > jQuery.bbq.getState( [ key ] [, coerce ] );
	//
	// Arguments:
	//
	//  key - (String) An optional state key for which to return a value.
	//  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
	//    undefined to their actual value. Defaults to false.
	//
	// Returns:
	//
	//  (Anything) If key is passed, returns the value corresponding with that key
	//    in the location.hash 'state', or undefined. If not, an object
	//    representing the entire 'state' is returned.

	jq_bbq.getState = jq_bbq_getState = function( key, coerce ) {
		return key === undefined || typeof key === 'boolean'
		? jq_deparam_fragment( key ) // 'key' really means 'coerce' here
		: jq_deparam_fragment( coerce )[ key ];
	};

	// Method: jQuery.bbq.removeState
	//
	// Remove one or more keys from the current browser history 'state', creating
	// a new state, setting location.hash and triggering any bound
	// <hashchange event> callbacks (provided the new state is different than
	// the previous state).
	//
	// If no arguments are passed, an empty state is created, which is just a
	// shortcut for jQuery.bbq.pushState( {}, 2 ).
	//
	// Usage:
	//
	// > jQuery.bbq.removeState( [ key [, key ... ] ] );
	//
	// Arguments:
	//
	//  key - (String) One or more key values to remove from the current state,
	//    passed as individual arguments.
	//  key - (Array) A single array argument that contains a list of key values
	//    to remove from the current state.
	//
	// Returns:
	//
	//  Nothing.
	//
	// Additional Notes:
	//
	//  * Setting an empty state may cause the browser to scroll.

	jq_bbq.removeState = function( arr ) {
		var state = {};

		// If one or more arguments is passed..
		if ( arr !== undefined ) {

			// Get the current state.
			state = jq_bbq_getState();

			// For each passed key, delete the corresponding property from the current
			// state.
			$.each( $.isArray( arr ) ? arr : arguments, function(i,v){
				delete state[ v ];
			});
		}

		// Set the state, completely overriding any existing state.
		jq_bbq_pushState( state, 2 );
	};

	// Event: hashchange event (BBQ)
	//
	// Usage in jQuery 1.4 and newer:
	//
	// In jQuery 1.4 and newer, the event object passed into any hashchange event
	// callback is augmented with a copy of the location.hash fragment at the time
	// the event was triggered as its event.fragment property. In addition, the
	// event.getState method operates on this property (instead of location.hash)
	// which allows this fragment-as-a-state to be referenced later, even after
	// window.location may have changed.
	//
	// Note that event.fragment and event.getState are not defined according to
	// W3C (or any other) specification, but will still be available whether or
	// not the hashchange event exists natively in the browser, because of the
	// utility they provide.
	//
	// The event.fragment property contains the output of <jQuery.param.fragment>
	// and the event.getState method is equivalent to the <jQuery.bbq.getState>
	// method.
	//
	// > $(window).bind( 'hashchange', function( event ) {
	// >   var hash_str = event.fragment,
	// >     param_obj = event.getState(),
	// >     param_val = event.getState( 'param_name' ),
	// >     param_val_coerced = event.getState( 'param_name', true );
	// >   ...
	// > });
	//
	// Usage in jQuery 1.3.2:
	//
	// In jQuery 1.3.2, the event object cannot to be augmented as in jQuery 1.4+,
	// so the fragment state isn't bound to the event object and must instead be
	// parsed using the <jQuery.param.fragment> and <jQuery.bbq.getState> methods.
	//
	// > $(window).bind( 'hashchange', function( event ) {
	// >   var hash_str = $.param.fragment(),
	// >     param_obj = $.bbq.getState(),
	// >     param_val = $.bbq.getState( 'param_name' ),
	// >     param_val_coerced = $.bbq.getState( 'param_name', true );
	// >   ...
	// > });
	//
	// Additional Notes:
	//
	// * Due to changes in the special events API, jQuery BBQ v1.2 or newer is
	//   required to enable the augmented event object in jQuery 1.4.2 and newer.
	// * See <jQuery hashchange event> for more detailed information.

	special[ str_hashchange ] = $.extend( special[ str_hashchange ], {

		// Augmenting the event object with the .fragment property and .getState
		// method requires jQuery 1.4 or newer. Note: with 1.3.2, everything will
		// work, but the event won't be augmented)
		add: function( handleObj ) {
			var old_handler;

			function new_handler(e) {
				// e.fragment is set to the value of location.hash (with any leading #
				// removed) at the time the event is triggered.
				var hash = e[ str_fragment ] = jq_param_fragment();

				// e.getState() works just like $.bbq.getState(), but uses the
				// e.fragment property stored on the event object.
				e.getState = function( key, coerce ) {
					return key === undefined || typeof key === 'boolean'
					? jq_deparam( hash, key ) // 'key' really means 'coerce' here
					: jq_deparam( hash, coerce )[ key ];
				};

				old_handler.apply( this, arguments );
			};

			// This may seem a little complicated, but it normalizes the special event
			// .add method between jQuery 1.4/1.4.1 and 1.4.2+
			if ( $.isFunction( handleObj ) ) {
				// 1.4, 1.4.1
				old_handler = handleObj;
				return new_handler;
			} else {
				// 1.4.2+
				old_handler = handleObj.handler;
				handleObj.handler = new_handler;
			}
		}

	});

})(jQuery,this);

/*!
* jQuery hashchange event - v1.3 - 7/21/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/

// Script: jQuery hashchange event
//
// *Version: 1.3, Last updated: 7/21/2010*
//
// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
// GitHub       - http://github.com/cowboy/jquery-hashchange/
// Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
// (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
//
// About: License
//
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
//
// About: Examples
//
// These working examples, complete with fully commented code, illustrate a few
// ways in which this plugin can be used.
//
// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
//
// About: Support and Testing
//
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
//
// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
//                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
// Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
//
// About: Known issues
//
// While this jQuery hashchange event implementation is quite stable and
// robust, there are a few unfortunate browser bugs surrounding expected
// hashchange event-based behaviors, independent of any JavaScript
// window.onhashchange abstraction. See the following examples for more
// information:
//
// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
//
// Also note that should a browser natively support the window.onhashchange
// event, but not report that it does, the fallback polling loop will be used.
//
// About: Release History
//
// 1.3   - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
//         "removable" for mobile-only development. Added IE6/7 document.title
//         support. Attempted to make Iframe as hidden as possible by using
//         techniques from http://www.paciellogroup.com/blog/?p=604. Added
//         support for the "shortcut" format $(window).hashchange( fn ) and
//         $(window).hashchange() like jQuery provides for built-in events.
//         Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
//         lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
//         and <jQuery.fn.hashchange.src> properties plus document-domain.html
//         file to address access denied issues when setting document.domain in
//         IE6/7.
// 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
//         from a page on another domain would cause an error in Safari 4. Also,
//         IE6/7 Iframe is now inserted after the body (this actually works),
//         which prevents the page from scrolling when the event is first bound.
//         Event can also now be bound before DOM ready, but it won't be usable
//         before then in IE6/7.
// 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
//         where browser version is incorrectly reported as 8.0, despite
//         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
// 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
//         window.onhashchange functionality into a separate plugin for users
//         who want just the basic event & back button support, without all the
//         extra awesomeness that BBQ provides. This plugin will be included as
//         part of jQuery BBQ, but also be available separately.

(function($,window,undefined){
	'$:nomunge'; // Used by YUI compressor.

	// Reused string.
	var str_hashchange = 'hashchange',

	// Method / object references.
	doc = document,
	fake_onhashchange,
	special = $.event.special,

	// Does the browser support window.onhashchange? Note that IE8 running in
	// IE7 compatibility mode reports true for 'onhashchange' in window, even
	// though the event isn't supported, so also test document.documentMode.
	doc_mode = doc.documentMode,
	supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );

	// Get location.hash (or what you'd expect location.hash to be) sans any
	// leading #. Thanks for making this necessary, Firefox!
	function get_fragment( url ) {
		url = url || location.href;
		return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
	};

	// Method: jQuery.fn.hashchange
	//
	// Bind a handler to the window.onhashchange event or trigger all bound
	// window.onhashchange event handlers. This behavior is consistent with
	// jQuery's built-in event handlers.
	//
	// Usage:
	//
	// > jQuery(window).hashchange( [ handler ] );
	//
	// Arguments:
	//
	//  handler - (Function) Optional handler to be bound to the hashchange
	//    event. This is a "shortcut" for the more verbose form:
	//    jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
	//    all bound window.onhashchange event handlers will be triggered. This
	//    is a shortcut for the more verbose
	//    jQuery(window).trigger( 'hashchange' ). These forms are described in
	//    the <hashchange event> section.
	//
	// Returns:
	//
	//  (jQuery) The initial jQuery collection of elements.

	// Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
	// $(elem).hashchange() for triggering, like jQuery does for built-in events.
	$.fn[ str_hashchange ] = function( fn ) {
		return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
	};

	// Property: jQuery.fn.hashchange.delay
	//
	// The numeric interval (in milliseconds) at which the <hashchange event>
	// polling loop executes. Defaults to 50.

	// Property: jQuery.fn.hashchange.domain
	//
	// If you're setting document.domain in your JavaScript, and you want hash
	// history to work in IE6/7, not only must this property be set, but you must
	// also set document.domain BEFORE jQuery is loaded into the page. This
	// property is only applicable if you are supporting IE6/7 (or IE8 operating
	// in "IE7 compatibility" mode).
	//
	// In addition, the <jQuery.fn.hashchange.src> property must be set to the
	// path of the included "document-domain.html" file, which can be renamed or
	// modified if necessary (note that the document.domain specified must be the
	// same in both your main JavaScript as well as in this file).
	//
	// Usage:
	//
	// jQuery.fn.hashchange.domain = document.domain;

	// Property: jQuery.fn.hashchange.src
	//
	// If, for some reason, you need to specify an Iframe src file (for example,
	// when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
	// do so using this property. Note that when using this property, history
	// won't be recorded in IE6/7 until the Iframe src file loads. This property
	// is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
	// compatibility" mode).
	//
	// Usage:
	//
	// jQuery.fn.hashchange.src = 'path/to/file.html';

	$.fn[ str_hashchange ].delay = 50;
	/*
	$.fn[ str_hashchange ].domain = null;
	$.fn[ str_hashchange ].src = null;
	*/

	// Event: hashchange event
	//
	// Fired when location.hash changes. In browsers that support it, the native
	// HTML5 window.onhashchange event is used, otherwise a polling loop is
	// initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
	// see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
	// compatibility" mode), a hidden Iframe is created to allow the back button
	// and hash-based history to work.
	//
	// Usage as described in <jQuery.fn.hashchange>:
	//
	// > // Bind an event handler.
	// > jQuery(window).hashchange( function(e) {
	// >   var hash = location.hash;
	// >   ...
	// > });
	// >
	// > // Manually trigger the event handler.
	// > jQuery(window).hashchange();
	//
	// A more verbose usage that allows for event namespacing:
	//
	// > // Bind an event handler.
	// > jQuery(window).bind( 'hashchange', function(e) {
	// >   var hash = location.hash;
	// >   ...
	// > });
	// >
	// > // Manually trigger the event handler.
	// > jQuery(window).trigger( 'hashchange' );
	//
	// Additional Notes:
	//
	// * The polling loop and Iframe are not created until at least one handler
	//   is actually bound to the 'hashchange' event.
	// * If you need the bound handler(s) to execute immediately, in cases where
	//   a location.hash exists on page load, via bookmark or page refresh for
	//   example, use jQuery(window).hashchange() or the more verbose
	//   jQuery(window).trigger( 'hashchange' ).
	// * The event can be bound before DOM ready, but since it won't be usable
	//   before then in IE6/7 (due to the necessary Iframe), recommended usage is
	//   to bind it inside a DOM ready handler.

	// Override existing $.event.special.hashchange methods (allowing this plugin
	// to be defined after jQuery BBQ in BBQ's source code).
	special[ str_hashchange ] = $.extend( special[ str_hashchange ], {

		// Called only when the first 'hashchange' event is bound to window.
		setup: function() {
			// If window.onhashchange is supported natively, there's nothing to do..
			if ( supports_onhashchange ) { return false; }

			// Otherwise, we need to create our own. And we don't want to call this
			// until the user binds to the event, just in case they never do, since it
			// will create a polling loop and possibly even a hidden Iframe.
			$( fake_onhashchange.start );
		},

		// Called only when the last 'hashchange' event is unbound from window.
		teardown: function() {
			// If window.onhashchange is supported natively, there's nothing to do..
			if ( supports_onhashchange ) { return false; }

			// Otherwise, we need to stop ours (if possible).
			$( fake_onhashchange.stop );
		}

	});

	// fake_onhashchange does all the work of triggering the window.onhashchange
	// event for browsers that don't natively support it, including creating a
	// polling loop to watch for hash changes and in IE 6/7 creating a hidden
	// Iframe to enable back and forward.
	fake_onhashchange = (function(){
		var self = {},
		timeout_id,

		// Remember the initial hash so it doesn't get triggered immediately.
		last_hash = get_fragment(),

		fn_retval = function(val){ return val; },
		history_set = fn_retval,
		history_get = fn_retval;

		// Start the polling loop.
		self.start = function() {
			timeout_id || poll();
		};

		// Stop the polling loop.
		self.stop = function() {
			timeout_id && clearTimeout( timeout_id );
			timeout_id = undefined;
		};

		// This polling loop checks every $.fn.hashchange.delay milliseconds to see
		// if location.hash has changed, and triggers the 'hashchange' event on
		// window when necessary.
		function poll() {
			var hash = get_fragment(),
			history_hash = history_get( last_hash );

			if ( hash !== last_hash ) {
				history_set( last_hash = hash, history_hash );

				$(window).trigger( str_hashchange );

			} else if ( history_hash !== last_hash ) {
				location.href = location.href.replace( /#.*/, '' ) + history_hash;
			}

			timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
		};

		
		// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
		// vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
		// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
		$.browser.msie && !supports_onhashchange && (function(){
			// Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
			// when running in "IE7 compatibility" mode.

			var iframe,
			iframe_src;

			// When the event is bound and polling starts in IE 6/7, create a hidden
			// Iframe for history handling.
			self.start = function(){
				if ( !iframe ) {
					iframe_src = $.fn[ str_hashchange ].src;
					iframe_src = iframe_src && iframe_src + get_fragment();

					// Create hidden Iframe. Attempt to make Iframe as hidden as possible
					// by using techniques from http://www.paciellogroup.com/blog/?p=604.
					iframe = $('<iframe tabindex="-1" title="empty"/>').hide()

					// When Iframe has completely loaded, initialize the history and
					// start polling.
					.one( 'load', function(){
						iframe_src || history_set( get_fragment() );
						poll();
					})

					// Load Iframe src if specified, otherwise nothing.
					.attr( 'src', iframe_src || 'javascript:0' )

					// Append Iframe after the end of the body to prevent unnecessary
					// initial page scrolling (yes, this works).
					.insertAfter( 'body' )[0].contentWindow;

					// Whenever `document.title` changes, update the Iframe's title to
					// prettify the back/next history menu entries. Since IE sometimes
					// errors with "Unspecified error" the very first time this is set
					// (yes, very useful) wrap this with a try/catch block.
					doc.onpropertychange = function(){
						try {
							if ( event.propertyName === 'title' ) {
								iframe.document.title = doc.title;
							}
						} catch(e) {}
					};

				}
			};

			// Override the "stop" method since an IE6/7 Iframe was created. Even
			// if there are no longer any bound event handlers, the polling loop
			// is still necessary for back/next to work at all!
			self.stop = fn_retval;

			// Get history by looking at the hidden Iframe's location.hash.
			history_get = function() {
				return get_fragment( iframe.location.href );
			};

			// Set a new history item by opening and then closing the Iframe
			// document, *then* setting its location.hash. If document.domain has
			// been set, update that as well.
			history_set = function( hash, history_hash ) {
				var iframe_doc = iframe.document,
				domain = $.fn[ str_hashchange ].domain;

				if ( hash !== history_hash ) {
					// Update Iframe with any initial `document.title` that might be set.
					iframe_doc.title = doc.title;

					// Opening the Iframe's document after it has been closed is what
					// actually adds a history entry.
					iframe_doc.open();

					// Set document.domain for the Iframe document as well, if necessary.
					domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' );

					iframe_doc.close();

					// Update the Iframe's hash, for great justice.
					iframe.location.hash = hash;
				}
			};

		})();
		// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
		// ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
		// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

		return self;
	})();

})(jQuery,this);
